package patternmining.model.construction;

import edu.uci.ics.jung.graph.DirectedSparseGraph;
import patternmining.model.graph.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/** Constructs a graph that models the received source (constructor parameter) as a abstraction following the BPSL-Formalism  
 *  The graph constructed is a computational representation of the BPSL-Formula encountered in the Source.
 * 
 *  Sources might be:
 *  -> BPSL formula specifications (instances of class BPSLSourceFile)
 *  -> Java Source code of a project (instances of class JavaSourceGroup) 
 * 
 * @see BPSLSourceFile.java
 * @see JavaSouceGroup.java
 * @version 1.0, 06.05.2008
 * @author Holger Wiehen
 */
public class ModelGraphBuilder {
   
   private Source source;
   private int edgeCount;
   private int vertexCount;
   private DirectedSparseGraph<Vertex, Edge> graph;
   private Map<String, Vertex> insertedVertices;
   private SourceReference sourceRef;
   private TypeMatcher typeMatcher;
   
   /**
    * 
    * @param source  the source that shall be modeled with a graph
    */
   public ModelGraphBuilder(Source source){
        this.source = source;
        edgeCount = 0;
        vertexCount = 0;
        graph = new DirectedSparseGraph<Vertex, Edge>();
        insertedVertices = new HashMap<String, Vertex>();
        sourceRef= new SourceReference();
        typeMatcher = new TypeMatcher();
   }
   
   /** Obtain the graph that representes computationaly the BPSL formula encountered
    * in the source.
    * 
    * @return
    */
   public DirectedSparseGraph<Vertex, Edge> getGraph(){
       TermData termData;
       AtomData atomData;
       //add all vertices to the graph
       ModelData modelData = source.modelData();
       Iterator<TermData> terms = modelData.termDataIterator();
       while( terms.hasNext() ){
           termData = terms.next();
           this.addVertex(termData.name(), termData.type() );
       }
       //add all edges to the graph
       Iterator<AtomData> atoms = modelData.atomDataIterator();
       while( atoms.hasNext() ){
           atomData = atoms.next();
           this.addEdge( atomData.origin(), atomData.destination(), atomData.type());
       }
       return graph;
   }
    
   private void addVertex( String name, BPSLTermType termType ){
       vertexCount++;
       Vertex vert = new Vertex(vertexCount, name, typeMatcher.termTypeToVertexType(termType), sourceRef);
       insertedVertices.put(name, vert);
       graph.addVertex(vert);
   }
   
   private void addEdge( String origin, String destination, BPSLPredicateType predicateType ){
       edgeCount++;
       Edge edge = new Edge( edgeCount, typeMatcher.predicateTypeToEdgeType(predicateType), sourceRef );
       graph.addEdge(edge, insertedVertices.get(origin), insertedVertices.get(destination));
   }
   
   
   /**Innerclass, with utility methods to transform BPSLTermType constants to VertexType constants
    * and BPSLPredicateType constants to EdgeType constants
    * 
    * The mapping is trivial (one-to-one) without any special lógic. But the sistem uses two different set of enums
    * to seperate between the source processing fase (this package), that consitutes conceptualy the recognition
    * of a BPSL Formula in the sources and the fase of subsequent work with the computational representation of the formula
    * as a graph.
    * 
    * So, proper enums for each fase exists to refelect the conceptual diference sintacticaly.
    * 
    * @author Holger Wiehen
    */
    private class TypeMatcher {

    VertexType termTypeToVertexType(BPSLTermType termType){
        VertexType vertexType = null;
        switch (termType){
            case CLASS :    vertexType = VertexType.CLASS;
                            break;
            case METHOD :   vertexType = VertexType.METHOD;
                            break;
            case TYPEDVARIABLE: vertexType = VertexType.TYPEDVARIABLE;        
        }
        return vertexType;
    }
    
   EdgeType predicateTypeToEdgeType( BPSLPredicateType bpslPredicate ){
       EdgeType edgeType = null;
       switch (bpslPredicate){
           case DefinedIn : edgeType = EdgeType.DEFINED_IN;
                            break;
           case ReferenceToOne : edgeType = EdgeType.REFERENCE_TO_ONE;
                            break;
           case ReferenceToMany : edgeType = EdgeType.REFERENCE_TO_MANY;
                            break;
           case Inheritance : edgeType = edgeType.INHERITANCE;
                            break;
           case Creation :  edgeType = EdgeType.CREATION;
                            break;
           case Invocation : edgeType = EdgeType.INVOCATION;
                            break;
           case Argument :  edgeType = EdgeType.ARGUMENT;
                            break;
           case ReturnType : edgeType = EdgeType.RETURN_TYPE;
       }
       return edgeType;
   }
   
}
    
}
